﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CriptoHash.Persistencia;
using CriptoHash.Criptografia;

namespace CriptoHash.Tests
{
    [TestClass]
    public class TesteFuncoesHash
    {
        private List<string> textosDiferentes;
        private List<string> textosMuitoParecidos;
        private List<string> textosSemiDiferentes;

        private Criptografador criptografador;

        private int Seed;

        [TestInitialize]
        public void TestInitialize()
        {
            textosDiferentes = ManipuladorTxt.ListaStringArquivo("Textos de Teste\\textosDiferentes.txt");
            textosMuitoParecidos = ManipuladorTxt.ListaStringArquivo("Textos de Teste\\textosMuitoParecidos.txt");
            textosSemiDiferentes = ManipuladorTxt.ListaStringArquivo("Textos de Teste\\textosSemiDiferentes.txt");
            criptografador = new Criptografador();

            Seed = Math.Abs((int)DateTime.Now.Ticks);
        }

        [TestMethod]
        public void TestLeituraArquivos()
        {
            Assert.AreEqual(true, textosDiferentes.Count > 0 && textosMuitoParecidos.Count > 0 && textosSemiDiferentes.Count > 0, "Arquivos de texto não foram lidos com sucesso!");
        }

        [TestMethod]
        public void TestCSharpComum()
        {
            string nomeMetodo = "C# Comum";

            bool testes = RealizarTestes(nomeMetodo);

            Assert.AreEqual(true, testes, nomeMetodo + " falhou na bateria de testes!");
        }

        [TestMethod]
        public void TestCongruenteS1()
        {
            string nomeMetodo = "Congruente S1";

            bool testes = RealizarTestes(nomeMetodo);

            Assert.AreEqual(true, testes, nomeMetodo + " falhou na bateria de testes!");
        }

        [TestMethod]
        public void TestCollatz()
        {
            string nomeMetodo = "Collatz";

            bool testes = RealizarTestes(nomeMetodo);

            Assert.AreEqual(true, testes, nomeMetodo + " falhou na bateria de testes!");
        }

        [TestMethod]
        public void TestSomaSenos()
        {
            string nomeMetodo = "Soma de Senos";

            bool testes = RealizarTestes(nomeMetodo);

            Assert.AreEqual(true, testes, nomeMetodo + " falhou na bateria de testes!");
        }

        [TestMethod]
        public void TestCRC16()
        {
            string nomeMetodo = "CRC-16";

            bool testes = RealizarTestes(nomeMetodo);

            Assert.AreEqual(true, testes, nomeMetodo + " falhou na bateria de testes!");
        }

        [TestMethod]
        public void TestTreze()
        {
            string nomeMetodo = "Treze";

            bool testes = RealizarTestes(nomeMetodo);

            Assert.AreEqual(true, testes, nomeMetodo + " falhou na bateria de testes!");
        }

        private bool RealizarTestes(string metodo)
        {
            //1 - Método para verificar se dada uma palavra qualquer, o hash será igual caso calculado duas vezes
            string palavra = "erupção";

            string hash1 = criptografador.ChamarMetodoHash(metodo, palavra);
            string hash2 = criptografador.ChamarMetodoHash(metodo, palavra);

            if (!hash1.Equals(hash2))
            {
                return false;
            }

            //2 - Verificar se a mudança de um caractere vai resultar em um hash diferente
            palavra = "eropção";
            hash2 = criptografador.ChamarMetodoHash(metodo, palavra);
            if (hash1.Equals(hash2))
            {
                return false;
            }

            //3 - Verificar se existe valor de hash para uma string vazia
            hash2 = criptografador.ChamarMetodoHash(metodo, "");
            if (null == hash2 || hash2.Equals(""))
            {
                return false;
            }

            //4 - Iniciar bateria de testes

            //4.1 - Testes para Textos Diferentes

            //Dicionário para guardar os valores já passados do Hash
            Dictionary<string, string> dicionarioHash = new Dictionary<string, string>();

            int valorEsperadoDiferentesHash = textosDiferentes.Count;
            string hashCalculado = "";

            foreach (string texto in textosDiferentes)
            {
                hashCalculado = criptografador.ChamarMetodoHash(metodo, texto);
                if (!dicionarioHash.ContainsKey(hashCalculado))
                {
                    dicionarioHash.Add(hashCalculado, texto); 
                }
            }

            if (valorEsperadoDiferentesHash != dicionarioHash.Count)
            {
                return false;
            }

            //4.2 - Testes para Textos Semi Diferentes

            //Dicionário para guardar os valores já passados do Hash
            dicionarioHash = new Dictionary<string, string>();

            valorEsperadoDiferentesHash = textosSemiDiferentes.Count;
            hashCalculado = "";

            foreach (string texto in textosSemiDiferentes)
            {
                hashCalculado = criptografador.ChamarMetodoHash(metodo, texto);
                if (!dicionarioHash.ContainsKey(hashCalculado))
                {
                    dicionarioHash.Add(hashCalculado, texto);
                }
            }

            if (valorEsperadoDiferentesHash != dicionarioHash.Count)
            {
                return false;
            }

            //4.3 - Testes para Textos Muito Parecidos

            //Dicionário para guardar os valores já passados do Hash
            dicionarioHash = new Dictionary<string, string>();

            valorEsperadoDiferentesHash = textosMuitoParecidos.Count;
            hashCalculado = "";

            foreach (string texto in textosMuitoParecidos)
            {
                hashCalculado = criptografador.ChamarMetodoHash(metodo, texto);
                if (!dicionarioHash.ContainsKey(hashCalculado))
                {
                    dicionarioHash.Add(hashCalculado, texto);
                }
            }

            if (valorEsperadoDiferentesHash != dicionarioHash.Count)
            {
                return false;
            }

            //5 - Realizar teste de stress

            int numeroTestes = 350;
            int numeroAcertos = RealizarTesteStress(numeroTestes, metodo);

            if (numeroAcertos < 0.95*numeroTestes)
            {
                return false;
            }

            return true;
        }

        private int RealizarTesteStress(int numeroTestes, string nomeMetodo)
        {
            Dictionary<string, string> hashComparativo = new Dictionary<string, string>();

            int numeroDiferentes = 0;
            RandomGenerator random = new RandomGenerator(Seed, 4000);

            for (int i = 0; i < numeroTestes; i++)
            {
                int numeroAleatorio = random.NumeroAleatorio(RandomGenerator.MetodoRandom.LINEAR_CONGRUENTE);
                string hash = criptografador.ChamarMetodoHash(nomeMetodo, numeroAleatorio);

                if (hashComparativo.ContainsKey(hash))
                {
                    //Se o hash der igual, mas o valor dos números também for igual, logo, a função está funcionando corretamente
                    if (hashComparativo[hash].Equals(numeroAleatorio))
                    {
                        numeroDiferentes++;
                    }
                }
                else
                {
                    //Se o hash não foi achado, ele é adicionado
                    numeroDiferentes++;
                    hashComparativo.Add(hash, numeroAleatorio.ToString());
                }
            }

            return numeroDiferentes;
        }
    }
}
